const { program } = require('commander');
const fs = require('fs');
const path = require('path');
const sharp = require('sharp');
const chalk = require('chalk');
const cliProgress = require('cli-progress');

// 支持的图片格式
const SUPPORTED_FORMATS = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp', '.svg'];

// 图片转换类
class ImageConverter {
  constructor(options = {}) {
    this.options = {
      quality: 80,
      ...options
    };
  }

  // 检查文件是否为支持的图片格式
  isSupportedImage(filePath) {
    const ext = path.extname(filePath).toLowerCase();
    return SUPPORTED_FORMATS.includes(ext);
  }

  // 转换单个图片文件
  async convertImage(inputPath, outputPath) {
    let orgiSize = 0;
    let convSize = 0;

    try {
      const sharpInstance = sharp(inputPath);
      
      // 获取图片信息
      const metadata = await sharpInstance.metadata();
      if( metadata.size ) {
        orgiSize = metadata.size;
      } else {
        orgiSize = fs.statSync(inputPath).size;
      }
      
      // 配置 WebP 转换选项
      const webpOptions = {
        quality: this.options.quality,
        lossless: false,
        nearLossless: false,
        smartSubsample: true,
        effort: 4
      };

      switch( metadata.format ) {
        case 'png':
        case 'webp':
        case 'gif':
          webpOptions.lossless = true;
          break;
        default:
          break;
      }

      const result = await sharpInstance
        .webp(webpOptions)
        .toFile(outputPath);

      if( result.size ) {
        convSize = result.size;
      } else {
        convSize = fs.statSync(outputPath).size;
      }

      return { status: true, orgiSize, convSize };
    } catch (error) {
      console.error(chalk.red(`转换失败 ${inputPath}: ${error.message}`));
      return { status: false, orgiSize, convSize };
    }
  }

  // 获取所有需要转换的图片文件
  async getImageFiles(inputPath, recursive = false) {
    const files = [];
    
    const processDirectory = async (dirPath, relativePath = '') => {
      const entries = await fs.promises.readdir(dirPath);
      
      for (const entry of entries) {
        const fullPath = path.join(dirPath, entry);
        const stat = await fs.promises.stat(fullPath);
        
        if (stat.isDirectory()) {
          if (recursive) {
            await processDirectory(fullPath, path.join(relativePath, entry));
          }
        } else if (stat.isFile() && this.isSupportedImage(fullPath)) {
          files.push({
            inputPath: fullPath,
            relativePath: path.join(relativePath, entry)
          });
        }
      }
    };

    await processDirectory(inputPath);
    return files;
  }

  // 生成输出文件路径
  generateOutputPath(inputFile, outputDir, inputDir) {
    const ext = path.extname(inputFile.relativePath);
    const nameWithoutExt = path.basename(inputFile.relativePath, ext);
    const dirPath = path.dirname(inputFile.relativePath);
    
    let outputPath;
    if (outputDir) {
      // 如果指定了输出目录，按照源文件夹结构创建
      outputPath = path.join(outputDir, dirPath, `${nameWithoutExt}.webp`);
    } else {
      // 如果没有指定输出目录，在源文件所在目录创建
      outputPath = path.join(path.dirname(inputFile.inputPath), `${nameWithoutExt}.webp`);
    }
    
    return outputPath;
  }

  // 确保目录存在
  async ensureDir(dirPath) {
    try {
      await fs.promises.mkdir(path.dirname(dirPath), { recursive: true });
    } catch (error) {
      // 目录可能已经存在，忽略错误
    }
  }

  // 转换文件或文件夹
  async convert(inputPath, outputPath, recursive = false) {
    try {
      // 检查输入路径是否存在
      if (!fs.existsSync(inputPath)) {
        console.error(chalk.red(`错误: 输入路径不存在: ${inputPath}`));
        return;
      }

      const stat = await fs.promises.stat(inputPath);
      
      if (stat.isFile()) {
        // 转换单个文件
        if (!this.isSupportedImage(inputPath)) {
          console.error(chalk.red(`不支持的图片格式: ${inputPath}`));
          return;
        }

        const output = outputPath || this.generateOutputPath(
          { inputPath, relativePath: path.basename(inputPath) },
          null,
          path.dirname(inputPath)
        );

        await this.ensureDir(output);
        console.log(chalk.blue(`正在转换: ${inputPath}`));
        
        const success = await this.convertImage(inputPath, output);
        if (success.status === true) {
          const saveRatio = ((success.orgiSize - success.convSize)/success.orgiSize).toFixed(2);
          console.log(chalk.green(`✓ 转换完成: ${output}, ${success.orgiSize} -> ${success.convSize} : ${saveRatio}`));
        }
        
      } else if (stat.isDirectory()) {
        // 转换文件夹
        console.log(chalk.blue(`正在扫描目录: ${inputPath}`));
        const imageFiles = await this.getImageFiles(inputPath, recursive);
        
        if (imageFiles.length === 0) {
          console.log(chalk.yellow('未找到支持的图片文件'));
          return;
        }

        console.log(chalk.blue(`找到 ${imageFiles.length} 个图片文件`));
        
        // 创建进度条
        const progressBar = new cliProgress.SingleBar({
          format: '转换进度 |' + chalk.cyan('{bar}') + '| {percentage}% || {value}/{total} 文件 || 当前: {filename}',
          barCompleteChar: '█',
          barIncompleteChar: '░',
          hideCursor: true
        });

        progressBar.start(imageFiles.length, 0, { filename: '' });

        const convDetail = [];

        let converted = 0;
        for (let i = 0; i < imageFiles.length; i++) {
          const file = imageFiles[i];
          const output = this.generateOutputPath(file, outputPath, inputPath);
          
          await this.ensureDir(output);
          progressBar.update(i + 1, { filename: path.basename(file.inputPath) });
          
          const success = await this.convertImage(file.inputPath, output);
          if (success.status === true) {
            converted++;
            convDetail.push({
              filename: path.basename(file.inputPath),
              orgiSize: success.orgiSize,
              convSize: success.convSize,
              saveRatio: ((success.orgiSize - success.convSize)/success.orgiSize).toFixed(2)
            });
          }
        }

        progressBar.stop();
        console.log(chalk.green(`\n✓ 转换完成! 成功转换 ${converted}/${imageFiles.length} 个文件`));

        let totalOrgiSize = 0;
        let totalConvSize = 0;
        for( let it of convDetail ) {
          if( it.convSize >= it.orgiSize ) {
            console.log(chalk.yellow(`- ${it.filename}, ${it.orgiSize} -> ${it.convSize} : ${it.saveRatio}`));
          } else {
            console.log(chalk.green(`- ${it.filename}, ${it.orgiSize} -> ${it.convSize} : ${it.saveRatio}`));
            
          }

          totalOrgiSize += it.orgiSize;
          totalConvSize += it.convSize;
        }

        const totalSaveRatio = ((totalOrgiSize - totalConvSize)/totalOrgiSize).toFixed(2);
        console.log(chalk.green(`\n✓ 转换完成! 成功转换 ${converted}/${imageFiles.length} 个文件, 总大小: ${totalOrgiSize} -> ${totalConvSize} : ${totalSaveRatio}`));
        
      } else {
        console.error(chalk.red(`无效的输入路径: ${inputPath}`));
      }
      
    } catch (error) {
      console.error(chalk.red(`错误: ${error.message}`));
    }
  }
}

// 命令行程序
program
  .name('imgconv')
  .description('图片格式转换工具，支持将各种格式的图片转换为 WebP 格式')
  .version('1.0.0');

program
  .argument('<input>', '要转换的文件或文件夹路径')
  .option('-o, --output <path>', '输出目录路径')
  .option('-r, --recursive', '递归处理子目录', false)
  .option('-q, --quality <number>', 'WebP 质量 (1-100)', '80')
  .action(async (input, options) => {
    try {
      const converter = new ImageConverter({
        quality: parseInt(options.quality) || 90
      });

      await converter.convert(input, options.output, options.recursive);
    } catch (error) {
      console.error(chalk.red(`程序执行错误: ${error.message}`));
      process.exit(1);
    }
  });

// 添加额外的帮助信息
program.addHelpText('after', `
使用示例:
  $ imgconv image.jpg                    # 转换单个文件
  $ imgconv ./images                     # 转换文件夹
  $ imgconv ./images -r                  # 递归转换文件夹
  $ imgconv ./images -o ./output         # 指定输出目录
  $ imgconv ./images -r -o ./output      # 递归转换并指定输出目录
  $ imgconv image.jpg -q 90              # 指定质量

支持的输入格式: JPG, JPEG, PNG, GIF, BMP, TIFF, WebP, SVG
输出格式: WebP
`);

program.parse(); 